// let obj = new Proxy({}, {
//     set: function (target, key, value, receiver) {
//         console.log(target, key, value, receiver, 'set');
//         return Reflect.set(target, key, value, receiver);
//     },
//     get: function(target, key, receiver) {
//         console.log(target, key, receiver, 'get');
//         return Reflect.get(target, key, receiver);
//     }
// });
// console.log(Reflect, 'dd');
// obj.a = 1;
// console.log(obj.b);

// var target = {};
// var handler = {};

// var object = { proxy: new Proxy(target, handler) };
// object.proxy.c = 'cc'
// console.log(object, 'object');

// let proxy = new Proxy({}, {
//     get: function(target, propKey) {
//       return 35;
//     }
// });

// let obj1 = Object.create(proxy);
// console.log(obj1, obj1.a)// 35

// var handler = {
//     get: function(target, name) {
//       if (name === 'prototype') {
//         return Object.prototype;
//       }
//       return 'Hello, ' + name;
//     },
  
//     apply: function(target, thisBinding, args) {
//         console.log(target, thisBinding, args, 'apply')
//       return args[0];
//     },
  
//     construct: function(target, args) {
//       return {value: args[1]};
//     }
// };
  
// var fproxy = new Proxy(function(x, y) {
//     return x + y;
// }, handler);

// console.log(fproxy(1, 2)) // 1
// console.log(new fproxy(1, 2)) // {value: 2}
// console.log(fproxy.prototype === Object.prototype) // true
// console.log(fproxy.foo === "Hello, foo") // true

// 获取数组的负数索引
// function createArray(...elements) {
//     let handler = {
//       get(target, propKey, receiver) {
//         let index = Number(propKey);
//         console.log(index, 'index')
//         if (index < 0) {
//           propKey = String(target.length + index);
//           console.log(propKey, 'jjss')
//         }
//         return Reflect.get(target, propKey, receiver);
//       }
//     };
  
//     let target = [];
//     target.push(...elements);
//     return new Proxy(target, handler);
// }
  
// let arr = createArray('a', 'b', 'c');
// console.log(arr[-1]); // c


// 链式操作
// var pipe = function (value) {
//     var funcStack = [];
//     var oproxy = new Proxy({} , {
//       get : function (pipeObject, fnName) {
//           console.log(pipeObject, fnName, 'fnName')
//         if (fnName === 'get') {
//           return funcStack.reduce(function (val, fn) {
//             return fn(val);
//           },value);
//         }
//         funcStack.push(window[fnName]);
//         console.log(funcStack, 'funcStack')
//         return oproxy;
//       }
//     });
  
//     return oproxy;
// }
// var double = n => n * 2;
// var pow    = n => n * n;
// var reverseInt = n => n.toString().split("").reverse().join("") | 0;
// console.log(pipe(3), 'jjhh')
// console.log(pipe(3).double.pow.reverseInt.get); // 63

// 链式拦截，生成dom
// const dom = new Proxy({}, {
//     get(target, property) {
//         console.log(target, property, 'property')
//       return function(attrs = {}, ...children) {
//         const el = document.createElement(property);
//         for (let prop of Object.keys(attrs)) {
//           el.setAttribute(prop, attrs[prop]);
//         }
//         for (let child of children) {
//           if (typeof child === 'string') {
//             child = document.createTextNode(child);
//           }
//           el.appendChild(child);
//         }
//         return el;
//       }
//     }
//   });
  
//   const el = dom.div({},
//     'Hello, my name is ',
//     dom.a({href: '//example.com'}, 'Mark'),
//     '. I like:',
//     dom.ul({},
//       dom.li({}, 'The web'),
//       dom.li({}, 'Food'),
//       dom.li({}, '…actually that\'s it')
//     )
// );
  
// document.body.appendChild(el);

// const proxy = new Proxy({}, {
//     get: function(target, key, receiver) {
//       return receiver;
//     }
// });
// console.log(proxy.getReceiver === proxy) // true


// 怎么使d.a === d
// const proxy = new Proxy({}, {
//     get: function(target, key, receiver) {
//       return receiver;
//     }
// });
  
// const d = Object.create(proxy);
// console.log(d.a === d) // true

// 不可写的obj的情况下会报错
// const target = Object.defineProperties({}, {
//     foo: {
//       value: 123,
//       writable: false,
//       configurable: false
//     },
// });
  
// const handler = {
//     get(target, propKey) {
//       return 'abc';
//     }
// };
  
// const proxy = new Proxy(target, handler);
  
// console.log(proxy.foo)

// set
// let validator = {
//     set: function(obj, prop, value) {
//       if (prop === 'age') {
//         if (!Number.isInteger(value)) {
//           throw new TypeError('The age is not an integer');
//         }
//         if (value > 200) {
//           throw new RangeError('The age seems invalid');
//         }
//       }
  
//       // 对于满足条件的 age 属性以及其他属性，直接保存
//       obj[prop] = value;
//       return true;
//     }
//   };
  
//   let person = new Proxy({}, validator);
  
//   person.age = 100;
  
//   console.log(person.age) // 100
//   person.age = 'young' // 报错
//   person.age = 300 // 报错

// 对象私有属性设置
// const handler = {
//     get (target, key) {
//       invariant(key, 'get');
//       return target[key];
//     },
//     set (target, key, value) {
//       invariant(key, 'set');
//       target[key] = value;
//       return true;
//     }
//   };
//   function invariant (key, action) {
//     if (key[0] === '_') {
//       throw new Error(`Invalid attempt to ${action} private "${key}" property`);
//     }
//   }
//   const target = {};
//   const proxy = new Proxy(target, handler);
//   console.log(proxy)
//   proxy._prop
  // Error: Invalid attempt to get private "_prop" property
//   proxy._prop = 'c'
  // Error: Invalid attempt to set private "_prop" property
  
// 对象某个属性不可写时，报错，值也不能修改
// const obj = {};
// Object.defineProperty(obj, 'foo', {
//   value: 'bar',
//   writable: false
// });

// const handler = {
//   set: function(obj, prop, value, receiver) {
//     obj[prop] = 'baz';
//     return true;
//   }
// };

// const proxy = new Proxy(obj, handler);
// proxy.foo = 'baz';
// console.log(proxy.foo) // "bar"

// 注意，set代理应当返回一个布尔值。严格模式下，set代理如果没有返回true，就会报错。
// 'use strict';
// const handler = {
//   set: function(obj, prop, value, receiver) {
//     obj[prop] = receiver;
//     // 无论有没有下面这一行，都会报错
//     return false;
//   }
// };
// const proxy = new Proxy({}, handler);
// proxy.foo = 'bar';
// TypeError: 'set' on proxy: trap returned falsish for property 'foo'

// apply
// var target = function () { return 'I am the target'; };
// var handler = {
//   apply: function () {
//     return 'I am the proxy';
//   }
// };

// var p = new Proxy(target, handler);

// console.log(p());

// apply拦截操作
// var twice = {
//     apply (target, ctx, args) {
//       return Reflect.apply(...arguments) * 2;
//     }
//   };
//   function sum (left, right) {
//     return left + right;
//   };
//   var proxy = new Proxy(sum, twice);
//   console.log(proxy(1, 2)) // 6
//   console.log(proxy.call(null, 5, 6)) // 22
//   console.log(proxy.apply(null, [7, 8])) // 30

// var handler = {
//     has (target, key) {
//       if (key[0] === '_') {
//         return false;
//       }
//       return key in target;
//     }
//   };
//   var target = { _prop: 'foo', prop: 'foo' };
//   var proxy = new Proxy(target, handler);
//   console.log('_prop' in proxy) // false
//   console.log(Reflect.has(proxy, '_props'))


// has用法
// var obj = { a: 10 };
// Object.preventExtensions(obj);

// var p = new Proxy(obj, {
//   has: function(target, prop) {
//     return false;
//   }
// });

// 'a' in p // TypeError is thrown

// 可以对一些属性进行过滤操作，但是对for...in不影响，只对in和has影响
// let stu1 = {name: '张三', score: 59};
// let stu2 = {name: '李四', score: 99};

// let handler = {
//   has(target, prop) {
//     if (prop === 'score' && target[prop] < 60) {
//       console.log(`${target.name} 不及格`);
//       return false;
//     }
//     return prop in target;
//   }
// }

// let oproxy1 = new Proxy(stu1, handler);
// let oproxy2 = new Proxy(stu2, handler);

// 'score' in oproxy1
// // 张三 不及格
// // false

// 'score' in oproxy2
// // true

// for (let a in oproxy1) {
//   console.log(oproxy1[a]);
// }
// // 张三
// // 59

// for (let b in oproxy2) {
//   console.log(oproxy2[b]);
// }
// // 李四
// // 99

// construct构造函数,
// 必须返回一个对象，否则报错
// const p = new Proxy(function () {}, {
//     construct: function(target, args) {
//       console.log('called: ' + args.join(', '));
//       return { value: args[0] * 10 };
//     }
// });
  
// console.log((new p(1)).value);

// 必须返回一个对象，否则报错
// const p = new Proxy(function() {}, {
//     construct: function(target, argumentsList) {
//       return 1;
//     }
// });
  
// new p() // 报错
// Uncaught TypeError: 'construct' on proxy: trap returned non-object ('1')


// 目标对象必须是函数，construct才可以生效
// const p = new Proxy({}, {
//     construct: function(target, argumentsList) {
//       return {};
//     }
// });
  
// new p() // 报错
// Uncaught TypeError: p is not a constructor

// construct()方法中的this指向的是handler，而不是实例对象
// const handler = {
//     construct: function(target, args) {
//         console.log(this === handler);
//         return new target(...args);
//     }
// };
// let p = new Proxy(function () {}, handler);
// new p() // true

// deleteProperty
// var handler = {
//     deleteProperty (target, key) {
//       invariant(key, 'delete');
//       delete target[key];
//       return true;
//     }
// };
// function invariant (key, action) {
//     if (key[0] === '_') {
//         throw new Error(`Invalid attempt to ${action} private "${key}" property`);
//     }
// }

// var target = { _prop: 'foo' };
// var proxy = new Proxy(target, handler);
// delete proxy._prop
// Error: Invalid attempt to delete private "_prop" property

// defineProperty, 这里操作被拦截，新增属性不会生效
// var handler = {
//     defineProperty (target, key, descriptor) {
//       return false;
//     }
// };
// var target = {};
// var proxy = new Proxy(target, handler);
// proxy.foo = 'bar' // 不会生效
// console.log(proxy)

// getOwnPropertyDescriptor
// var handler = {
//     getOwnPropertyDescriptor (target, key) {
//         if (key[0] === '_') {
//             return;
//         }
//         return Object.getOwnPropertyDescriptor(target, key);
//     }
// };
// var target = { _foo: 'bar', baz: 'tar' };
// var proxy = new Proxy(target, handler);
// console.log(Object.getOwnPropertyDescriptor(proxy, 'wat'));
// // undefined
// console.log(Object.getOwnPropertyDescriptor(proxy, '_foo'));
// // undefined
// console.log(Object.getOwnPropertyDescriptor(proxy, 'baz'));
// { value: 'tar', writable: true, enumerable: true, configurable: true }

// getPrototypeOf
// var proto = {};
// var p = new Proxy({}, {
//   getPrototypeOf(target) {
//     return proto;
//   }
// });
// console.log(Object.getPrototypeOf(p))
// console.log(Object.getPrototypeOf(p) === proto) // true


// isExtensible, 判断是否可扩展拦截Object.isExtensible
// var p = new Proxy({}, {
//     isExtensible: function(target) {
//       console.log("called");
//       return true;
//     }
// });
  
// Object.isExtensible(p)
// "called"
// true

// 这个方法有一个强限制，它的返回值必须与目标对象的isExtensible属性保持一致，否则就会抛出错误。
// var p = new Proxy({}, {
//     isExtensible: function(target) {
//         return false;
//     }
// });
  
// Object.isExtensible(p)
// Uncaught TypeError: 'isExtensible' on proxy: trap result does not reflect extensibility of proxy target (which is 'true')

// ownKeys 拦截以下操作
// bject.getOwnPropertyNames()
// Object.getOwnPropertySymbols()
// Object.keys()
// for...in循环
// let target = {
//     a: 1,
//     b: 2,
//     c: 3
//   };
  
//   let handler = {
//     ownKeys(target) {
//         console.log('own')
//         return ['a'];
//     }
//   };
  
//   let proxy = new Proxy(target, handler);
  
//   Object.keys(proxy)
//   // [ 'a' ]

//   for (let k in proxy) {}


// 可以用于属性过滤
// let target = {
// _bar: 'foo',
// _prop: 'bar',
// prop: 'baz'
// };

// let handler = {
// ownKeys (target) {
//     console.log(target, 'target')
//     return Reflect.ownKeys(target).filter(key => key[0] !== '_');
// }
// };

// let proxy = new Proxy(target, handler);
// for (let key of Object.keys(proxy)) {
// console.log(target[key]);
// }
// "baz"

// let target = {
//     a: 1,
//     b: 2,
//     c: 3,
//     [Symbol.for('secret')]: '4',
// };
  
// Object.defineProperty(target, 'key', {
//     enumerable: false,
//     configurable: true,
//     writable: true,
//     value: 'static'
// });

// let handler = {
//     ownKeys(target) {
//         return ['a', 'd', Symbol.for('secret'), 'key'];
//     }
// };

// let proxy = new Proxy(target, handler);

// console.log(Object.keys(proxy));
// ['a']

// preventExtensions
// 这个方法有一个限制，只有目标对象不可扩展时（即Object.isExtensible(proxy)为false），proxy.preventExtensions才能返回true，否则会报错。
// var proxy = new Proxy({}, {
//     preventExtensions: function(target) {
//       return true;
//     }
// });
  
// Object.preventExtensions(proxy)
// Uncaught TypeError: 'preventExtensions' on proxy: trap returned truish but the proxy target is extensible

// 解决办法
// 通常要在proxy.preventExtensions()方法里面，调用一次Object.preventExtensions()
// var proxy = new Proxy({}, {
//     preventExtensions: function(target) {
//       console.log('called');
//       Object.preventExtensions(target);
//       return true;
//     }
// });

// setPrototypeOf 
// 拦截 Object.setPrototypeOf()
// var handler = {
//     setPrototypeOf (target, proto) {
//       throw new Error('Changing the prototype is forbidden');
//     }
// };
// var proto = {};
// var target = function () {};
// var proxy = new Proxy(target, handler);
// Object.setPrototypeOf(proxy, proto);
// Error: Changing the prototype is forbidden

// revocable
let target = {};
let handler = {};

let {proxy, revoke} = Proxy.revocable(target, handler);

proxy.foo = 123;
proxy.foo // 123

revoke();
proxy.foo // TypeError: Revoked
// Proxy.revocable()的一个使用场景是，目标对象不允许直接访问，必须通过代理访问，一旦访问结束，就收回代理权，不允许再次访问。

